package cgl.narada.benchmark;

import cgl.narada.event.DistributionTraces;
import cgl.narada.event.NBEvent;
import cgl.narada.event.TemplateProfileAndSynopsisTypes;
import cgl.narada.event.impl.DistributionTracesImpl;
import cgl.narada.event.impl.NBEventGenerator;
import cgl.narada.protocol.Destinations;
import cgl.narada.service.ServiceException;
import cgl.narada.service.client.ClientService;
import cgl.narada.service.client.EventProducer;
import cgl.narada.service.client.SessionService;
import cgl.narada.service.timer.ClockI;
import cgl.narada.service.timer.HRClock;
import cgl.narada.util.ByteUtilities;

public class CreationAndMarshallingOverheads {

   private ClientService clientService;

   private EventProducer producer;

   private int entityId = 1000;

   private int templateId = 3142;

   private String contentSynopsis = "Sports/NBA";

   private int contentSynopsisType = TemplateProfileAndSynopsisTypes.STRING;

   private ClockI hrClock;

   private int numOfTimes = 1;
   
   public CreationAndMarshallingOverheads() throws ServiceException {
      String configFile = "C:/NB/NaradaBrokering/config/ServiceConfiguration.txt";
      SessionService.setServiceConfigurationLocation(configFile);
      clientService = SessionService.getClientService(entityId, configFile);
      producer = clientService.createEventProducer();
      producer.setTemplateId(templateId);

      hrClock = HRClock.getClock();
   }

   public NBEvent createNBEvent(int payloadSize) throws ServiceException {

      byte[] contentPayload = new byte[payloadSize];

      
      NBEvent nbEvent = producer.generateEvent(contentSynopsisType,
            contentSynopsis, contentPayload);
      return nbEvent;
   }

   
   
   public void benchmarkEventCreation(int payloadSize) throws ServiceException {
      long startTime = hrClock.getTimeMicroseconds();

      for (int i = 0; i < numOfTimes; i++) {
         byte[] contentPayload = new byte[payloadSize];

         NBEvent nbEvent = producer.generateEvent(contentSynopsisType,
               contentSynopsis, contentPayload);
      }
      long stopTime = hrClock.getTimeMicroseconds();

      long elapsedTime = stopTime - startTime;
      double meanDelay = (double) elapsedTime / (double) numOfTimes;
      String report = "Mean delay for event of size [" + payloadSize
                      + "] = " + meanDelay;
      System.out.println(report);
   }

   
   public void benchmarkAlternateSerialization(int payloadSize) {
      long startTime = hrClock.getTimeMicroseconds();
      
      byte[] dataBytes = new byte[payloadSize];
      //ByteBuffer byteBuffer = ByteBuffer.allocate(payloadSize + 200);
      //byteBuffer.putInt(payloadSize);
      //byteBuffer.put(dataBytes);
      //byte[] serialBytes = byteBuffer.array(); 
      byte[] marshalledBytes = new byte[payloadSize + 300];
      
      System.arraycopy(dataBytes, 0, marshalledBytes, 300, dataBytes.length);
      
      long stopTime = hrClock.getTimeMicroseconds();
      long elapsedTime = stopTime - startTime;
      
      String report = "Time for alternate serialization =" + elapsedTime;
      System.out.println(report);
   }
   
   public void benchmarkInvocations(int payloadSize) {
      
      byte[] dataBytes = new byte[payloadSize];
      
      long startTime = hrClock.getTimeMicroseconds();
      
      methodWithArrayArguments1(dataBytes);
      methodWithArrayArguments1(dataBytes);
      
      long stopTime = hrClock.getTimeMicroseconds();
      long elapsedTime = stopTime - startTime;
      
      String report = "Time for 5 invocations =" + elapsedTime;
      System.out.println(report);
      
      
   }
   
   public void methodWithArrayArguments1(byte[] x) {
      byte[] y = x;
      methodWithArrayArguments2(y);
   }
   
   public void methodWithArrayArguments2(byte[] x) {
      byte[] y = x;
      methodWithArrayArguments3(y);
   }
   
   public void methodWithArrayArguments3(byte[] x) {
      byte[] y = x;
      methodWithArrayArguments4(y);
   }
   
   public void methodWithArrayArguments4(byte[] x) {
      byte[] y = x;
      methodWithArrayArguments5(y);
   }
   
   public void methodWithArrayArguments5(byte[] x) {
      byte[] y = x;
   }
   
   
   public void benchmarkMarshalling(int payloadSize) throws ServiceException {
      NBEvent nbEvent = createNBEvent(payloadSize);

      long startTime = hrClock.getTimeMicroseconds();
      

      for (int i = 0; i < numOfTimes; i++) {
         byte[] nbBytes = nbEvent.getBytes();
      }
      long stopTime = hrClock.getTimeMicroseconds();

      long elapsedTime = stopTime - startTime;
      double meanDelay = (double) elapsedTime / (double) numOfTimes;
      String report = "Mean delay for serilaizing payload [" + payloadSize
                      + "] = " + meanDelay;
      System.out.println(report);
   }

   public void benchmarkUnmarshalling(int payloadSize) throws ServiceException {
      NBEvent originalEvent = createNBEvent(payloadSize);
      byte[] nbBytes = originalEvent.getBytes();
      

      long startTime = hrClock.getTimeMicroseconds();
      for (int i = 0; i < numOfTimes; i++) {
         NBEvent nbEvent = NBEventGenerator.unmarshallEvent(nbBytes);
      }
      long stopTime = hrClock.getTimeMicroseconds();

      long elapsedTime = stopTime - startTime;
      double meanDelay = (double) elapsedTime / (double) numOfTimes;
      String report = "Mean delay for unmarshalling payload [" + payloadSize
                      + "] = " + meanDelay;
      System.out.println(report);
   }
   
   public void testMarshalling() throws ServiceException {
      String testString = "abcdefghijklmnopqrstuvwxyz1234567890";
      
      byte[] contentPayload = testString.getBytes();
      
      NBEvent nbEvent = producer.generateEvent(contentSynopsisType,
            contentSynopsis, contentPayload);
      
      byte[] nbBytes = nbEvent.getBytes();
      NBEvent nbEvent2 = NBEventGenerator.unmarshallEvent(nbBytes);
      Destinations destOne = new Destinations(3);
      byte[] destOneBytes = destOne.getDestinationsInBytes();
      System.out.println("Destination Bytes Length = " + destOneBytes.length);
      for (int i=0; i < destOneBytes.length; i++) {
         destOneBytes[i] = 1;
      }
      
      byte[] destTwoBytes = destOne.getDestinationsInBytes();
      for (int i=0; i < destTwoBytes.length; i++) {
         destTwoBytes[i] = 2;
      }
      DistributionTraces traces = new DistributionTracesImpl();
      
      traces.setDestinationsToReach(destOneBytes);
      traces.setDestinationsTraversedSoFar(destTwoBytes);
      cgl.narada.event.impl.NBEventGenerator.updateDistributionTraces(nbEvent2,
            traces);
      nbEvent2.getBytes();
      
      
      byte[] payload = nbEvent2.getContentPayload();
      System.out.println(testString + "\n" + new String(payload));
      
      
      
      if (payload.length != contentPayload.length) {
         System.out.println("Payload MISMATCH!!!");
         return;
      } 
      
      for (int i=0; i<payload.length; i++) {
         if (payload[i] != contentPayload[i]) {
            System.out.println("Payload MISMATCH!!! at [" + i + "]" );
            return;
         }
      }
      
      System.out.println("Payloads Match PERFECTLY!\n\n");
      
      int dest = -1;
      String report = "Value -1 is represented as " + ByteUtilities.printInt(dest);
      System.out.println(report);
      
      byte[] destBytes = new byte[16];
      for (int i=0; i< 16; i++) {
         destBytes[i] = 2;
         System.out.print(destBytes[i]);
      }
      
      int level = 2;
      System.out.println("Message received over a level (" + level + ") link!");
      for (int i=level-1; i >= 0; i--) {
         System.out.println("Compute destinations at level [" + i +"]");
      }
      
   }
   
   /**
    * @param args
    */
   public static void main(String[] args) {
      // TODO Auto-generated method stub
      String module = "CreationAndMarshallingOverheads.main() ->";

      try {
         CreationAndMarshallingOverheads overheads = new CreationAndMarshallingOverheads();
         for (int i = 3; i <= 4; i++) {
            int payloadSize = i * 100000;
            //overheads.benchmarkEventCreation(payloadSize);
            //overheads.benchmarkAlternateSerialization(payloadSize);
            //overheads.benchmarkInvocations(payloadSize);
            //overheads.benchmarkMarshalling(payloadSize);
            //overheads.benchmarkUnmarshalling(payloadSize);
            overheads.testMarshalling();
            System.out.println();
         }

      } catch (ServiceException se) {
         System.out.println(module + "");
      }

   }

}
